package com.et114.components.acegi.afterinvocation;

import org.acegisecurity.*;
import org.acegisecurity.acl.basic.NamedEntityObjectIdentity;
import org.acegisecurity.afterinvocation.AfterInvocationProvider;
import org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;

import com.et114.components.acegi.acl.AclCreator;
import com.et114.components.acegi.acl.AclUtils;

import java.util.Iterator;
import java.util.List;

/**
 * * 当有acl对象被创建的时候，需要创建该对象对应的acl信息。
 * @author guanhw  
 * @see AfterInvocationProvider
 */
public class CreatAclEntryAfterInvocationProvider implements
		AfterInvocationProvider , InitializingBean , MessageSourceAware {
	// ~ Static fields/initializers
	// =============================================

	protected static final Log		logger							= LogFactory
																			.getLog( BasicAclEntryAfterInvocationProvider.class );
	// ~ Instance fields
	// ========================================================

	private Class					processDomainObjectClass;

	protected MessageSourceAccessor	messages						= AcegiMessageSource.getAccessor();
	private Class					defaultAclObjectIdentityClass	= NamedEntityObjectIdentity.class;
	private String					processConfigAttribute			= "AFTER_ACL_CREAT";
	private List					creators;

	public List getCreators ( ) {
		return creators;
	}

	/**
	 * 创建domain对象acl权限信息的执行者是Creator，每个不同的creator可能对应不同的domain，或使用不同的创建规则。
	 * @param newList
	 */
	public void setCreators ( List newList ) {
		checkIfValidList( newList );

		Iterator iter = newList.iterator();

		while ( iter.hasNext() ) {
			Object currentObject = null;

			try {
				currentObject = iter.next();

				//AclCreator attemptToCast = ( AclCreator ) currentObject;
			} catch ( ClassCastException cce ) {
				throw new IllegalArgumentException( "AclCreator "
						+ currentObject.getClass().getName()
						+ " must implement AclCreator" );
			}
		}

		this.creators = newList;
	}

	/**
	 * @param defaultAclObjectIdentityClass
	 */
	public void setDefaultAclObjectIdentityClass (
			Class defaultAclObjectIdentityClass ) {
		this.defaultAclObjectIdentityClass = defaultAclObjectIdentityClass;
	}

	public Class getDefaultAclObjectIdentityClass ( ) {
		return defaultAclObjectIdentityClass;
	}

	public Class getProcessDomainObjectClass ( ) {
		return processDomainObjectClass;
	}

	public void setProcessDomainObjectClass ( Class processDomainObjectClass ) {
		this.processDomainObjectClass = processDomainObjectClass;
	}

	public void afterPropertiesSet ( ) throws Exception {
		Assert.notNull( processConfigAttribute , "A processConfigAttribute is mandatory" );
		Assert.notNull( messages , "A message source must be set" );
		Assert.notNull( processDomainObjectClass , "processDomainObjectClass is mandatory" );
		Assert.notNull( defaultAclObjectIdentityClass , "defaultAclObjectIdentityClass required" );

		try {
			defaultAclObjectIdentityClass.getConstructor( new Class [ ] { Object.class } );
		} catch ( NoSuchMethodException nsme ) {
			throw new IllegalArgumentException(
					"defaultAclObjectIdentityClass must provide a constructor that accepts the domain object instance!" );
		}
	}

	public String getProcessConfigAttribute ( ) {
		return processConfigAttribute;
	}

	public void setProcessConfigAttribute ( String processConfigAttribute ) {
		this.processConfigAttribute = processConfigAttribute;
	}

	public void setMessageSource ( MessageSource messageSource ) {
		this.messages = new MessageSourceAccessor( messageSource );
	}

	public Object decide ( Authentication authentication , Object object ,
			ConfigAttributeDefinition config , Object returnedObject )
			throws AccessDeniedException {
		Iterator iter = config.getConfigAttributes();

		while ( iter.hasNext() ) {
			ConfigAttribute attr = ( ConfigAttribute ) iter.next();
			if ( this.supports( attr ) ) {
				// 取得domain对象，即刚刚创建的对象
				Object domainObject = AclUtils.getDomainObjectInstance( object , processDomainObjectClass );
				Iterator cit = this.creators.iterator();
				while ( cit.hasNext() ) {
					AclCreator creator = ( AclCreator ) cit.next();
					if ( creator.supports( domainObject, returnedObject ) ) {
						// 创建acl规则信息
						creator.creat( authentication, domainObject, config,
								returnedObject );
						break;
					}
				}
			}
		}
		return returnedObject;
	}

	public boolean supports ( ConfigAttribute attribute ) {
		return ( attribute.getAttribute() != null )
				&& attribute.getAttribute()
						.equals( getProcessConfigAttribute() );
	}

	/**
	 * @param clazz the secure object
	 * @return always <code>true</code>
	 */
	public boolean supports ( Class clazz ) {
		return true;
	}

	private void checkIfValidList ( List listToCheck ) {
		if ( ( listToCheck == null ) || ( listToCheck.size() == 0 ) ) {
			throw new IllegalArgumentException(
					"A list of AfterInvocationProviders is required" );
		}
	}
}
